﻿//此文件用于查找明场图中关于损伤的数据
#include <opencv2/opencv.hpp>
#include <thread>
#include <stack>
#include <ctime>
#include <filesystem>
#include "public.h"
#include "FinderData.hpp"

std::vector<std::string> args;
std::vector<cv::Mat> mats;
int mat_idx = 0;//显示当前使用的是哪个图
int pen_size = 1;//笔画带下
std::string ui_title = "ui";//窗口标题
cv::Mat ui;//显示窗口数据
cv::Mat color_mark;//图片颜色标记png
cv::Rect ROI;//当前显示区域的roi
std::stack<cv::Rect> ROI_stack;
FinderData finderData;
uint16_t painting_color = 0;
bool painting_mode = false;
float ui_scale = 1;

#define ENTER 13
#define UP 2490368
#define DOWN 2621440
#define LEFT 2424832
#define RIGHT 2555904
#define ESC 27


void MouseCallback(int event, int x, int y, int flags, void *userdata);
void ui_render(void);
void ui_renderColor(void);
int event_loop();
int main(int argn, const char **argv) {
    destructHelper destructhelper([] {
        printf("程序退出中.....\r\n");
    });//保证退出的时候能析构
    printf("//处理参数列表\r\n");
    if (argn == 1) {
        return 0;
    } else if (strcmp(argv[1], "--test") == 0) {
        args.emplace_back(R"(C:\Users\17616\Desktop\本科毕设\实验数据记录\2021-5-20\G0_0-F4-0520\M-G0_0-Afterpng.png)");
        args.emplace_back(R"(C:\Users\17616\Desktop\本科毕设\实验数据记录\2021-5-20\G0_0-F4-0520\M-G0_0-After-x3png.png)");
        args.emplace_back(R"(C:\Users\17616\Desktop\本科毕设\实验数据记录\2021-5-20\G0_0-F4-0520\M-G0_0-After-x9png.png)");
        args.emplace_back(R"(C:\Users\17616\Desktop\本科毕设\实验数据记录\2021-5-20\G0_0-F4-0520\M-G0_0-C1-Afterpng.png)");
    } else {
        for (int i = 1; i < argn; ++i) {
            args.emplace_back(argv[i]);
        }
    }
    printf("//载入图片到内存...\r\n");
    mats.resize(args.size());
    thread_pool tp(0, args.size());
    tp.post([&](int start, int end) {
        for (int num = start; num < end; ++num) {
            mats[num] = cv::imread(args[num], cv::IMREAD_COLOR);
            //如果发现有错误提前结束线程
            if (mats[num].cols == 0 || mats[num].rows == 0) { return; }
            printf("Load  %s\r\n", GetFileName(args[num]).c_str());
        }
    });
    tp.join();//等待线程池执行完毕
    printf("//载入图片到内存 √\r\n");

    printf("//检查所有图片的size and type...");
    if (mats[0].cols == 0 || mats[0].rows == 0) { return -1; }
    for (int i = 1; i < mats.size(); ++i) {
        if (mats[i].size != mats[0].size ||
            mats[i].type() != mats[0].type()) { return -1; }
    }
    printf("\t√\r\n");

    printf("//加载颜色标记以及xlsx\r\n");
    color_mark = cv::imread(GetFilePath(args[0]) + "FinderWorkSpace.png", cv::IMREAD_UNCHANGED);
    if (color_mark.empty() || color_mark.size != mats[0].size) {
        color_mark = cv::Mat::zeros(mats[0].size(), CV_16U);
    }
    destructhelper.push_back([]() {
        cv::imwrite(GetFilePath(args[0]) + "FinderWorkSpace.png", color_mark);
    });
    finderData.open(GetFilePath(args[0]) + "FinderWorkSpace.xlsx");
    printf("现在一共有%d个损伤点记录\r\n", finderData.size());
    destructhelper.push_back([]() {
        finderData.save();
    });
    if (finderData.size() == 0) {
        finderData.addRow();
    }
    painting_color = finderData("编号", -1).value().get<int>();


    printf("//加载ui");
    ROI.x = 0;
    ROI.y = 0;
    ROI.height = mats[0].size().height;
    ROI.width = mats[0].size().width;
    ui_scale = MAX((float) mats[0].size().height / 1400, (float) mats[0].size().width / 2880);
    cv::namedWindow("ui", cv::WINDOW_NORMAL);
    cv::setMouseCallback("ui", MouseCallback, NULL);
    ui_renderColor();
    cv::imshow("ui", ui);
    cv::resizeWindow("ui", (float) mats[mat_idx].size().width / ui_scale, (float) mats[mat_idx].size().height / ui_scale);
    printf("\t√\r\n");
    printf("进入事件循环\r\n");
    return event_loop();
}

void ui_render(void) {
    if (ui.size() != mats[0].size() || ui.type() != CV_8UC3) {
        ui = mats[0].clone();
    }
    mats[mat_idx](ROI).copyTo(ui(ROI));//仅拷贝ROI区域的
    ui_title = GetFileName(args[mat_idx]);
    cv::setWindowTitle("ui", ui_title);
}

void ui_renderColor(void) {
    if (ui.size() != mats[0].size() || ui.type() != CV_8UC3) {
        ui = mats[0].clone();
    }
    auto func = [&](int s, int e) {
        for (int r = s; r < e; ++r) {
            uint16_t *pix_color_mark = (uint16_t *) ((size_t) color_mark.data + r * color_mark.step) + ROI.x;
            cv::Vec3b *pix_mat = (cv::Vec3b *) ((size_t) mats[mat_idx].data + r * mats[mat_idx].step) + ROI.x;
            cv::Vec3b *pix_ui = (cv::Vec3b *) ((size_t) ui.data + r * ui.step) + ROI.x;
            for (int c = ROI.x; c < ROI.x + ROI.width; ++c) {
                if (*pix_color_mark == 0) {
                    *pix_ui = *pix_mat;
                } else {

                    int16_t H = finderData("H", finderData.idxOFref(*pix_color_mark)).value().get<int>();//H
                    int16_t S = finderData("S", finderData.idxOFref(*pix_color_mark)).value().get<int>();//S C
                    int16_t V = (*pix_mat)[2];//V
                    auto C = S;
//                    float H_ = H / 60;
                    auto X = (C * (60 - std::abs(H % 180 - 60))) / 60;

                    int16_t R = V - C;
                    int16_t G = V - C;
                    int16_t B = V - C;
                    if (H < 60) {
                        R += C;
                        G += X;
                    } else if (H < 120) {
                        R += X;
                        G += C;
                    } else if (H < 180) {
                        G += C;
                        B += X;
                    } else if (H < 240) {
                        G += X;
                        B += C;
                    } else if (H < 300) {
                        R += X;
                        B += C;
                    } else {
                        R += C;
                        B += X;
                    }
                    (*pix_ui)[0] = B < 0 ? 0 : (B > 255 ? 255 : B);
                    (*pix_ui)[1] = G < 0 ? 0 : (G > 255 ? 255 : G);
                    (*pix_ui)[2] = R < 0 ? 0 : (R > 255 ? 255 : R);
                }
                pix_color_mark++;
                pix_mat++;
                pix_ui++;
            }
        }
    };
//    func(ROI.y, ROI.y + ROI.height);
//    渲染颜色
    thread_pool tp(ROI.y, ROI.y + ROI.height);
    tp.post(func);
    tp.join();
    ui_title = GetFileName(args[mat_idx]);
    cv::setWindowTitle("ui", ui_title);
}

int event_loop() {
    int key;
    std::string str;
    while (1) {
        key = cv::waitKeyEx();
        switch (key) {
            case LEFT:
                mat_idx = MAX(0, mat_idx - 1);
                break;
            case RIGHT:
                mat_idx = MIN(mats.size() - 1, mat_idx + 1);
                break;
            case UP:
                ui_scale *= 0.9;
                cv::resizeWindow("ui", (float) mats[mat_idx].size().width / ui_scale, (float) mats[mat_idx].size().height / ui_scale);
                break;
            case DOWN:
                ui_scale *= 1.1;
                cv::resizeWindow("ui", (float) mats[mat_idx].size().width / ui_scale, (float) mats[mat_idx].size().height / ui_scale);
                break;
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                pen_size = key - '0';
                break;
            case 'p':
                painting_mode = true;
                printf("painting模式启动\r\n");
                break;
            case 's':
                painting_mode = false;
                printf("painting模式关闭\r\n");
                break;
            case ENTER:
                printf("进入命令行模式，请输入指令\r\n");
                char inputkey;
                while (1) {
                    printf("当前最后一个编号是%d\r\n", finderData("编号", -1).value().get<int>());
                    printf("painting_color = %d\r\n", painting_color);
                    std::cout << "n 添加|c 修改画笔颜色|q 退出 :" << std::endl;
                    std::cin >> inputkey;
//                    inputkey = getchar();
                    if (inputkey == 'n') {
                        finderData.addRow();
                        painting_color = finderData("编号", -1).value().get<int>();
                    } else if (inputkey == 'c') {
                        std::cout << "输入颜色:";
                        std::cin >> painting_color;
                    } else {
                        break;
                    }
                }
                printf("当前最后一个编号是%d\r\n", finderData("编号", -1).value().get<int>());
                printf("painting_color = %d\r\n", painting_color);
                printf("退出命令行\r\n");
                break;
            case -1:
                return 0;
            default:
                printf("key = %d\r\n", key);
                break;
        }
        ui_renderColor();
        cv::imshow("ui", ui(ROI));
    }
    return 0;
}

void MouseCallback(int event, int x, int y, int flags, void *userdata) {
    int big_x = x + ROI.x;
    int big_y = y + ROI.y;
    //以上是计算在大图当中的坐标
    int WheelDelta;

    static int drag_x;//计算用于拖动的起点
    static int drag_y;
    static bool drag_flag = false;//拖动状态

    static bool draw_flag = false;//画画状态
    static bool eraser_flag = false;
    static clock_t drag_last_time;
    static auto painting = [&](uint16_t color) {
        if (pen_size < 2) {
            color_mark.at<uint16_t>(big_y, big_x) = color;
        } else {
            for (int i = 0; i < pen_size; ++i) {
                for (int j = 0; j < pen_size; ++j) {
                    color_mark.at<uint16_t>(big_y + i, big_x + j) = color;
                }
            }
        }
    };
#define CLOCKS_PER_FRAME (CLOCKS_PER_SEC/60)
    switch (event) {
        case cv::EVENT_LBUTTONDOWN:
            if (painting_mode) {
                draw_flag = true;
                painting(painting_color);
                ui_renderColor();
                cv::imshow("ui", ui(ROI));
            }
            if (flags & cv::EVENT_FLAG_CTRLKEY) {
                printf("(%d,%d),ref = %d\r\n", big_y, big_x, color_mark.at<uint16_t>(big_y, big_x));
            }
            break;
        case cv::EVENT_LBUTTONUP:
            if (painting_mode) {
                draw_flag = false;
                painting(painting_color);
                ui_renderColor();
                cv::imshow("ui", ui(ROI));
            }

            break;
        case cv::EVENT_RBUTTONDOWN:
            if (painting_mode) {
                eraser_flag = true;
                painting(0);
                ui_renderColor();
                cv::imshow("ui", ui(ROI));
            }
            break;
        case cv::EVENT_RBUTTONUP:
            if (painting_mode) {
                eraser_flag = false;
                painting(0);
                ui_renderColor();
                cv::imshow("ui", ui(ROI));
            }
            break;
        case cv::EVENT_MOUSEWHEEL:
            WheelDelta = cv::getMouseWheelDelta(flags);
            if (WheelDelta >= 0) {
                int width = ROI.width;
                int height = ROI.height;
                if (width > 10 && height > 10) {
                    //放大图片
                    ROI_stack.push(ROI);//保存当前ROI
                    //计算ROI区域
                    int x1 = x - (x) / 2;
                    int x2 = x + (width - x) / 2;
                    int y1 = y - (y) / 2;
                    int y2 = y + (height - y) / 2;
                    ROI = cv::Rect(ROI.x + x1, ROI.y + y1, MAX(x2 - x1, 0), MAX(y2 - y1, 0));
                    ui_renderColor();
                    cv::imshow("ui", ui(ROI));
                }
                std::cout << "Wheel=" << WheelDelta << ' ' << ROI << std::endl;
            } else {
                if (!ROI_stack.empty()) {
                    //缩小
                    ROI = ROI_stack.top();
                    ROI_stack.pop();
                    std::cout << "Wheel=" << WheelDelta << ' ' << ROI << std::endl;
                    ui_renderColor();
                    cv::imshow("ui", ui(ROI));
                }
            }
            break;
        case cv::EVENT_MBUTTONDOWN:
            drag_flag = true;
            drag_x = ROI.x + x;
            drag_y = ROI.y + y;
            drag_last_time = clock();
            break;
        case cv::EVENT_MBUTTONUP:
            drag_flag = false;
            //退出拖动时再重绘制一下
            ROI.x = -x + drag_x;
            ROI.y = -y + drag_y;
            //限制ROI到合法区域
            ROI.x = MAX(0, ROI.x);
            ROI.x = MIN(mats[mat_idx].size().width - ROI.width, ROI.x);
            ROI.y = MAX(0, ROI.y);
            ROI.y = MIN(mats[mat_idx].size().height - ROI.height, ROI.y);
            ui_renderColor();
            cv::imshow("ui", ui(ROI));
            break;
        case cv::EVENT_MOUSEMOVE:
            if (drag_flag && clock() - drag_last_time > CLOCKS_PER_FRAME) {
                drag_last_time = clock();
                ROI.x = -x + drag_x;
                ROI.y = -y + drag_y;
                //限制ROI到合法区域
                ROI.x = MAX(0, ROI.x);
                ROI.x = MIN(mats[mat_idx].size().width - ROI.width, ROI.x);
                ROI.y = MAX(0, ROI.y);
                ROI.y = MIN(mats[mat_idx].size().height - ROI.height, ROI.y);
                ui_renderColor();
                cv::imshow("ui", ui(ROI));
            }
            if (draw_flag) { painting(painting_color); }
            if (eraser_flag) { painting(0); }
            if ((draw_flag || eraser_flag) && clock() - drag_last_time > CLOCKS_PER_FRAME) {
                drag_last_time = clock();
                ui_renderColor();
                cv::imshow("ui", ui(ROI));
            }
            break;
        default:
            break;
    }
}
